package com.bolt.report.engine;

import com.bolt.common.bean.BeanUtil;
import com.bolt.common.collection.CollectionUtil;
import com.bolt.common.collection.IteratorUtil;
import com.bolt.common.collection.MapUtil;
import com.bolt.common.convert.Convert;
import com.bolt.common.reflect.ObjectUtil;
import com.bolt.common.security.Md5Util;
import com.bolt.common.utils.DateUtil;
import com.bolt.common.utils.NumberUtil;
import com.bolt.common.utils.SpringContextUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.report.config.DefaultReportTokenService;
import com.bolt.report.config.ReportTokenService;
import com.bolt.report.definition.*;
import com.bolt.report.engine.fill.*;
import com.bolt.report.engine.model.Cell;
import com.bolt.report.engine.model.Expression;
import com.bolt.report.engine.model.ReportPrint;
import com.bolt.report.engine.query.QueryExecuteFactory;
import com.bolt.support.sqlconfig.base.SqlStatement;
import com.bolt.support.sqlconfig.builder.XmlSqlConfigBuilder;
import com.googlecode.aviator.AviatorEvaluator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ObjectFactory {
    private ObjectFactory() {
    }

    public static Cell createTextCell(Object text, StyleDefinition style, int rowIndex, int cellIndex, FillContext fillContext) {
        FillTextElement textElement = new FillTextElement(text, fillContext);
        Cell cell = new Cell(rowIndex, cellIndex, textElement);
        cell.setStyle(style);
        return cell;
    }

    public static Cell createBlankCell(int rowIndex, int cellIndex, FillContext fillContext) {
        FillTextElement textElement = new FillTextElement(StrUtil.EMPTY, fillContext);
        Cell cell = new Cell(rowIndex, cellIndex, textElement);
        cell.setStyle(new StyleDefinition());
        return cell;
    }


    public static Cell createCell(CellDefinition element, FillContext fillContext, BindData bindData, int rowIndex, int cellIndex) {
        FillElement fillElement = null;
        Cell cell = null;
        Expression expression = element.getExpressionObj(bindData);
        switch (element.getElementType()) {
            case TEXT:
                fillElement = new FillTextElement(fillContext, expression, element.getName());
                break;
            case CHART:
                fillElement = new FillChartElement(element.getJsonData(), fillContext);
                break;
            case CROSSTAB:
                fillElement = new FillImageElement(element, fillContext);
                break;
            case IMG:
                fillElement = new FillImageElement(element, bindData, fillContext);
                break;
            case BARCODE:
                fillElement = new FillCodeImageElement(ElementType.BARCODE, element.getJsonData(), element, bindData, fillContext);
                break;
            case QRCODE:
                fillElement = new FillCodeImageElement(ElementType.QRCODE, element.getJsonData(), element, bindData, fillContext);
                break;
            case DATASET:
                fillElement = new FillDataSetElement(fillContext, bindData, expression);
                break;
            default:
                fillElement = new FillTextElement(fillContext, expression, element.getName());
        }
        fillElement.setHyperlinkData(element.getHyperlinks());
        cell = new Cell(rowIndex, cellIndex, fillElement);
        cell.setStyle(element.getStyle());
        cell.setColSpan(element.getColSpan());
        cell.setRowSpan(cell.getRowSpan());

        cell.accept(fillContext.getElementVisitor());
        return cell;
    }


    public static ReportPrint createReportPrint(ReportDefinition definition) {
        ReportPrint reportPrint = new ReportPrint();
        reportPrint.setName(definition.getReportFullName());
        reportPrint.setTopMargin(definition.getPaper().getTopMargin());
        reportPrint.setLeftMargin(definition.getPaper().getLeftMargin());
        reportPrint.setBottomMargin(definition.getPaper().getBottomMargin());
        reportPrint.setRightMargin(definition.getPaper().getRightMargin());
        reportPrint.setPrintOrderValue(definition.getPaper().getPrintOrderValue());
        reportPrint.setPaper(definition.getPaper());
        if (IteratorUtil.isNotEmpty(definition.getSortDefinitions())) {
            List<BRSortField> sortFields = new ArrayList<>();
            for (SortDefinition sortDefinition : definition.getSortDefinitions()) {
                BRSortField sortField = new FillSortField(sortDefinition.getName(), sortDefinition.getSortOrder());
                sortFields.add(sortField);
            }
            reportPrint.setSortFields(sortFields);
        }
        List<BRGroupField> groupFields = new ArrayList<>();
        if (definition.haveElements()) {
            List<RowDefinition> rowDefinitions = CollectionUtil.newArrayList(definition.getElements());
            if (definition.haveHead()) {
                rowDefinitions.addAll(definition.getHeads());
            }
            for (RowDefinition rowDefinition : rowDefinitions) {
                List<CellDefinition> groupDefinitions = definition.getGroupCell(rowDefinition.getCellDefinitions());
                for (CellDefinition cellDefinition : groupDefinitions) {
                    FillGroupField groupField = new FillGroupField(cellDefinition.getKey(), cellDefinition.getName(), cellDefinition.getDataExpand(), cellDefinition.getSortOrder(), cellDefinition);
                    groupFields.add(groupField);
                }
            }
        }
        if (IteratorUtil.isNotEmpty(groupFields)) {
            reportPrint.setGroup(groupFields);
        }
        return reportPrint;
    }

    public static FillDataSet createFillDataSet(DataSetDefinition dataSetDefinition, Map<String, Object> parameters) {

        List<FieldDefinition> fieldDefinitions = dataSetDefinition.getFields();
        List<FillField> fillFields = new ArrayList<>(fieldDefinitions.size());
        List<BRSortField> sortFields = new ArrayList<>();
        for (FieldDefinition fieldDefinition : fieldDefinitions) {
            FillField fillField = new FillField();
            fillField.setName(fieldDefinition.getName());
            fillField.setValueClass(fieldDefinition.getClass());
            fillField.setExpression(new Expression(fieldDefinition.getExpr()));
            if (fieldDefinition.getSortOrder() != SortOrder.NONE) {
                sortFields.add(new FillSortField(fieldDefinition.getName(), fieldDefinition.getSortOrder()));
            }
            fillFields.add(fillField);
        }
        return new FillDataSet(fillFields, sortFields, dataSetDefinition, parameters);
    }

    public static String generateParamKey(Map<String, Object> param) {
        param.remove(ReportConstants.PAGE_INDEX);
        String key = MapUtil.joinIgnoreNull(param, "=", ",");
        return Md5Util.md5(key);
    }

    public static QueryExecuteFactory getQueryExecuterFactory() {
        return SpringContextUtil.getBean(QueryExecuteFactory.class);
    }

    public static ReportFiller createFiller(ReportDefinition reportDefinition) {
        if (reportDefinition.haveGroup()) {
            return new GroupReportFiller(reportDefinition);
        } else {
            return new DefaultReportFiller(reportDefinition);
        }

    }


    public static Object convertParameter(Object value, ParameterType parameterType) {
        if (ObjectUtil.isNull(value)) {
            return value;
        }
        if (!(value instanceof String)) {
            return value;
        }
        String data = (String) value;
        switch (parameterType) {
            case STR:
                return value;
            case DATE:
                return DateUtil.parseDate(data);
            case INT:
                if (NumberUtil.isNumber(data)) {
                    return Convert.toInt(value);
                } else {
                    return value;
                }
            case DOUBLE:
                if (NumberUtil.isNumber(data)) {
                    return Convert.toDouble(value);
                } else {
                    return value;
                }
            case COLLECTION:
                return StrUtil.split(data, ',');
            default:
                return value;
        }
    }

    public static Object getValueByExpression(String expression, Object data) {
        try {
            if (StrUtil.isNotBlank(expression)) {
                return AviatorEvaluator.execute(expression, BeanUtil.beanToMap(data));
            } else {
                return data;
            }
        } catch (Exception ex) {
            return StrUtil.EMPTY;
        }
    }

    public static SqlStatement parseSqlText(String sql) {
        StringBuilder querySql = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        querySql.append("<sql>");
        querySql.append(sql);
        querySql.append("</sql>");
        XmlSqlConfigBuilder xmlSqlConfigBuilder = new XmlSqlConfigBuilder();
        return xmlSqlConfigBuilder.parseStrXml(querySql.toString());
    }


    public static <E> ArrayList<E> newEmptyValueList(int size) {
        ArrayList list = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            list.add(null);
        }
        return list;
    }


    public static ReportTokenService getTokenService() {
        ReportTokenService reportTokenService = SpringContextUtil.getBean(ReportTokenService.class);
        if (ObjectUtil.isNull(reportTokenService)) {
            reportTokenService = new DefaultReportTokenService();
        }
        return reportTokenService;
    }


}